package server;

import java.util.Date;
import java.util.Set;

import javax.ejb.EJB;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import phasebook.client.DirtyRegionManager;
import phasebook.client.DirtyRegionManager.DirtyRegion;
import phasebook.client.DirtyRegionManager.Region;
import phasebook.client.PhasebookWSClientFactory;
import phasebook.client.RemotePhasebookService;
import phasebook.client.msgs.MessageInfo;
import phasebook.client.msgs.PhotoInfo;
import phasebook.client.msgs.UserInfo;
import phasebook.client.msgs.UserProfileInfo;
import client.api.CombinedResponse;
import client.api.PhasebookService;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class PhasebookRPCServlet extends RemoteServiceServlet implements PhasebookService, HttpSessionListener{
	private static final long serialVersionUID = -5590156991469221786L;

	RemotePhasebookService service = PhasebookWSClientFactory.create("http://localhost:8080/phasebook_esb/http/Phasebook/WebWsEntry");
			//PhasebookWSClientFactory.create("http://localhost:8080/phasebook-impl-0.0.1-SNAPSHOT/PhasebookServiceImpl");
	
	private RemotePhasebookService getService() {
		if (regionManager!=null) // running in production with EJB injection
			return service;
		else //for web-development use this dummy service
			return DevPhasebookService.getInstance();
	}
	
	@EJB
	DirtyRegionManager regionManager;
	
	private DirtyRegionManager getRegionManager() {
		if (regionManager!=null) // running in production with EJB injection
			return regionManager;
		else //for web-development use this dummy service
			return DevPhasebookService.getInstance();
	}
	
	@Override
	public CombinedResponse registerUser(UserProfileInfo req) throws Exception {
		return initSession(getService().registerUser(req));
	}

	@Override
	public CombinedResponse waitForRegions() {
		long myId = getUserId();
		Set<DirtyRegion> dirtyRegions = getRegionManager().waitForDirtyRegions(myId);
		CombinedResponse resp = new CombinedResponse();
		if (dirtyRegions!=null)
			for (DirtyRegion region : dirtyRegions) {
				switch (region.getRegion()) {
				case Album:
					resp.addPhotos(getService().getAlbum(myId));
					break;
				case Board:
					resp.addMessages(getService().getMessages(region.getMyId(),region.getUserId()));
					break;
				case FinantialMovements:
					resp.addMovements(getService().getFinancialMovements(myId));
					break;
				case FriendRequests:
					resp.addFriendRequests(getService().getFriendRequests(myId));
					break;
				case Friends:
					resp.addFriends(getService().getFriends(myId));
					break;
				case MyRequests:
					resp.addMyFriendRequests(getService().getMyFriendRequests(myId));
					break;
				case Profile:
					resp.addProfiles(getService().getProfile(region.getUserId()));
					break;
				}
			}
		return resp;
	}

	private long getUserId() {
		return (Long) super.perThreadRequest.get().getSession().getAttribute("userId");
	}
	
	private CombinedResponse initSession(long myUserId){
		HttpSession session = super.perThreadRequest.get().getSession();
		session.setAttribute("userId", myUserId);
		getRegionManager().subscribeSelf(myUserId);
		return new CombinedResponse()
			.setUserId(myUserId)
			.addFriends(getService().getFriends(myUserId))
			.addFriendRequests(getService().getFriendRequests(myUserId))
			.addMyFriendRequests(getService().getMyFriendRequests(myUserId))
			.addMessages(getService().getMessages(myUserId,myUserId))
			.addPhotos(getService().getAlbum(myUserId))
			.addProfiles(getService().getProfile(myUserId))
			.addMovements(getService().getFinancialMovements(myUserId));
	}
	
	@Override
	public void sessionCreated(HttpSessionEvent hse) {
		
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent hse) {
		Long userId = (Long) hse.getSession().getAttribute("userId");
		if (userId!=null)
			getRegionManager().unsubscribeAll(userId);
	}

	@Override
	public CombinedResponse validateLogin(String userName, String password) throws Exception {
		long validateLogin = getService().validateLogin(userName, password);
		System.out.println("validateLogin: "+validateLogin);
		return initSession(
				validateLogin
		);
	}
	
	@Override
	public CombinedResponse trackUser(long userId) throws Exception {
		getRegionManager().subscribeOtherUser(getUserId(), userId);
		return new CombinedResponse()
			.addMessages(getService().getMessages(getUserId(),userId))
			.addProfiles(getService().getProfile(userId));
	}

	@Override
	public CombinedResponse untrackUser(long userId) throws Exception {
		getRegionManager().unsubscribeOtherUser(getUserId(), userId);
		return new CombinedResponse();
	}

	@Override
	public CombinedResponse findUsers(String regEx) throws Exception {
		return new CombinedResponse()
			.addFoundUsers(getService().findUsers(getUserId(), regEx));
	}

	@Override
	public CombinedResponse updateUser(UserProfileInfo user) throws Exception {
		getService().updateUser(user);
		return new CombinedResponse();
	}

	@Override
	public CombinedResponse updateBalance(double balanceUpdate, long accountId) throws Exception {
		getService().updateBalance(getUserId(), balanceUpdate, accountId);
		getRegionManager().publishDirtyRegions(getUserId(), Region.Profile,Region.FinantialMovements);
		return new CombinedResponse();
	}	
	
	@Override
	public CombinedResponse publishMessage(long boardOwnerId, String message, boolean isPrivate, Long photoId) throws Exception{
		getService().publishMessage(
				new MessageInfo( 
					0,
					new UserInfo(getUserId(), null),
					new UserInfo(boardOwnerId, null),
					message,
					new Date(),
					photoId!=null?new PhotoInfo(photoId,null,null):null,
					isPrivate
				)
		);
		getRegionManager().publishDirtyRegions(boardOwnerId, Region.Board);
		return new CombinedResponse()
				.addMessages(getService().getMessages(getUserId(),boardOwnerId));
	}

	@Override
	public CombinedResponse deleteMessage(long messageId) throws Exception {
		getService().deleteMessage(getUserId(), messageId);
		getRegionManager().publishDirtyRegions(getUserId(),Region.Board);
		return new CombinedResponse()
			.addMessages(getService().getMessages(getUserId(), getUserId()));
	}

	@Override
	public CombinedResponse requestFriendship(long friendId) throws Exception {
		getService().requestFriendship(getUserId(), friendId);
		getRegionManager().publishDirtyRegions(friendId,Region.FriendRequests);
		return new CombinedResponse()
			.addMyFriendRequests(getService().getMyFriendRequests(getUserId()));
	}

	@Override
	public CombinedResponse cancelFriendship(long friendId) throws Exception {
		getService().cancelFriendship(getUserId(), friendId);
		getRegionManager().publishDirtyRegions(friendId,Region.Friends,Region.Board);
		return new CombinedResponse()
			.addFriends(getService().getFriends(getUserId()));
	}

	@Override
	public CombinedResponse cancelFriendshipRequest(long friendId) throws Exception {
		getService().cancelFriendshipRequest(getUserId(), friendId);
		getRegionManager().publishDirtyRegions(friendId,Region.FriendRequests);
		return new CombinedResponse()
			.addMyFriendRequests(getService().getMyFriendRequests(getUserId()));
	}

	@Override
	public CombinedResponse denyFriendshipRequest(long friendId) throws Exception {
		getService().denyFriendshipRequest(getUserId(), friendId);
		getRegionManager().publishDirtyRegions(friendId,Region.MyRequests);
		return new CombinedResponse()
			.addFriendRequests(getService().getFriendRequests(getUserId()));
	}

	@Override
	public CombinedResponse acceptFriendshipRequest(long friendId) throws Exception {
		getService().acceptFriendshipRequest(getUserId(), friendId);
		getRegionManager().publishDirtyRegions(friendId,Region.MyRequests,Region.Friends,Region.Board);
		return new CombinedResponse()
			.addFriendRequests(getService().getFriendRequests(getUserId()))
			.addFriends(getService().getFriends(getUserId()));
	}

	@Override
	public CombinedResponse placeBet(double ammount,int result) throws Exception {
		getService().placeBet(getUserId(), ammount,result);
		return new CombinedResponse()
			.addProfiles(getService().getProfile(getUserId()))
			.addMovements(getService().getFinancialMovements(getUserId()));
	}	
}
